
module Main where

import Text.Printf -- Oba, Haskell tem printf! :-)
import Data.List
import System.Random
import System.IO.Unsafe

type Point     = (Float,Float)
type Color     = (Int,Int,Int)
type Circle    = (Point,Float)

imageWidth :: Int
imageWidth = 1920

imageHeight :: Int
imageHeight = 1080

rndColor :: IO Int
rndColor = randomRIO(0,255)

rndRadius :: IO Int
rndRadius = randomRIO(1,4)

-- Funcao principal que faz leitura do dataset e gera arquivo SVG
main :: IO ()
main = do
        strcontent <- readFile infile
        let pairs = map (span (/= ' ')) (lines strcontent)
            freq0 = readInts (map snd pairs)
            freqs = reverse (sort freq0)
        writeFile outfile (svgCloudGen imageWidth imageHeight freqs)
        putStrLn "Ok!"
        where
                infile = "dataset.txt"
                outfile = "tagcloud.svg"


-- Transforma lista de strings em lista de inteiros
readInts :: [String] -> [Int]
readInts ss = map read ss



-- Gera o documento SVG da tag cloud, concatenando cabecalho, conteudo e rodape
svgCloudGen :: Int -> Int -> [Int] -> String
svgCloudGen w h dataset =
        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" ++
        "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" ++
        (svgViewBox w h) ++
        (concat (svgBubbleGen dataset)) ++ 
--        (geraTexto ((fromIntegral w/2) - (retornaRaio (proxRaio dataset)*0.10)) (fromIntegral h/2) ("Teste bubble")) ++
        "</svg>\n"


-- Gera os circulos, passando 0 para a funcao saber que o primeiro circulo deve ser posicionado no meio da figura
svgBubbleGen:: [Int] -> [String]
svgBubbleGen dataset = bubbles dataset 0 0 0


-- Nao pude usar o unsafePerformIO dentro de uma funcao que retorna Int, senao o numero nao varia como desejado.
bubbles :: [Int] -> Int -> Float -> Float -> [String]
bubbles [] _ _ _= []
bubbles (x:xs) c a t
                  | c == 0 = [svgCircle (calculaPonto 0 0 (fromIntegral x) 0 0 0) (unsafePerformIO rndColor,unsafePerformIO rndColor,unsafePerformIO rndColor)] ++ bubbles xs x 5 8
                  | otherwise = [svgCircle (calculaPonto a t (fromIntegral x) a t (fromIntegral c)) (unsafePerformIO rndColor,unsafePerformIO rndColor,unsafePerformIO rndColor)] ++ bubbles xs x a t                  

calculaPonto :: Float -> Float -> Float -> Float -> Float -> Float -> Circle
calculaPonto a t raio a2 t2 raio2
            | (raio*0.20) > 200.0 = calculaPonto a t (raio - (fromIntegral (unsafePerformIO rndRadius)/2)) a2 t2 raio2
            | a == 0 && t == 0 = (((fromIntegral imageWidth/2),(fromIntegral imageHeight/2)),(raio*0.20) + 12.5)
            | (checkIntersection (((abs (pontoX a2 t2))+(fromIntegral imageWidth/2),(abs (pontoY a2 t2))+(fromIntegral imageHeight/2)),(raio2*0.20) + 12.5)(((abs (pontoX a t))+(fromIntegral imageWidth/2),(abs (pontoY a t))+(fromIntegral imageHeight/2)),(raio*0.20) + 12.5)) == False = 
                  (((abs (pontoX a t))+(fromIntegral imageWidth/2),(abs (pontoY a t))+(fromIntegral imageHeight/2)),(raio*0.20) + 12.5)
            | otherwise = (calculaPonto a (t+0.1) raio a2 t2 raio2)

pontoX :: Float -> Float -> Float
pontoX a t = a * radians t * cos  (radians t)

pontoY :: Float -> Float -> Float
pontoY a t = a * radians t * sin  (radians t)

-- Converte graus pra radianos
radians :: (Floating a) => a -> a
radians n = n / (360 / (2 * pi))

checkIntersection :: Circle -> Circle -> Bool
checkIntersection ((x1,y1),r1) ((x2,y2),r2)
                  | rect > sumRadius = False
                  | otherwise = True
                  where rect = sqrt (((x2-x1)^2) + ((y2-y1)^2))
                        sumRadius = (r1+r2)


-- Gera string representando um circulo em SVG. 
svgCircle :: Circle -> Color -> String
svgCircle ((x,y),raio) (r,g,b) = printf "<circle cx=\"%f\" cy=\"%f\" r=\"%f\" fill=\"rgb(%d,%d,%d)\" />\n" x y raio r g b

geraTexto :: Float -> Float -> String -> String
geraTexto x y dataset = printf "<text x=\"%f\"  y=\"%f\" style=\"stroke:none; fill:#000000;\">%s</text>" x y dataset


-- Configura o viewBox da imagem e coloca retangulo branco no fundo
svgViewBox :: Int -> Int -> String
svgViewBox w h =
        printf  "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\"" w h w h ++
                " version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n" ++
        printf "<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" style=\"fill:white;\"/>\n" w h